// 文件路径: dbms/cluster/ClusterManager.java
package com.dbms.cluster;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * 集群管理器 (Singleton)。
 * 负责加载和管理集群中所有节点的配置信息。
 * 这是一个单例类，确保在整个节点进程中只有一个统一的集群视图。
 *
 * 【已增强】
 * 新增了对主从（Master-Slave）模式的配置支持。可以从配置文件中读取集群模式，
 * 并识别出哪个是Master节点，哪些是Slave节点。
 */
public class ClusterManager {

    /**
     * 定义集群支持的两种工作模式。
     */
    public enum ClusterMode {
        SHARDING,       // 数据分片模式
        MASTER_SLAVE    // 主从复制模式
    }

    // 单例实例
    private static ClusterManager instance;

    // 当前节点自身的信息
    private final NodeInfo selfNode;
    // 存储集群中所有节点信息的Map，键为节点ID
    private final Map<String, NodeInfo> clusterNodes;

    // --- 【新增】主从模式相关字段 ---
    private final ClusterMode clusterMode; // 当前集群的工作模式
    private NodeInfo masterNode; // Master节点的信息
    private final Map<String, NodeInfo> slaveNodes = new HashMap<>(); // 所有Slave节点的信息

    /**
     * 私有构造函数，防止外部直接实例化。
     * @param selfNodeId 当前节点的ID。
     */
    private ClusterManager(String selfNodeId) {
        this.clusterNodes = new HashMap<>();
        // 加载配置文件 `cluster.properties`，并确定集群模式
        this.clusterMode = loadClusterConfig(selfNodeId);

        // 从加载的节点中找到代表自己的节点信息
        this.selfNode = clusterNodes.get(selfNodeId);
        if (this.selfNode == null) {
            throw new IllegalArgumentException("当前节点 '" + selfNodeId + "' 的配置未在 cluster.properties 中找到。");
        }

        // 如果是主从模式，进行额外的配置验证
        if (this.clusterMode == ClusterMode.MASTER_SLAVE) {
            if (this.masterNode == null) {
                throw new IllegalStateException("主从模式配置错误: 必须指定一个 'master' 节点。");
            }
            if (this.slaveNodes.isEmpty()) {
                // 这是一个警告而非错误，因为一个主节点可以暂时没有从节点
                System.err.println("警告: 主从模式下未配置任何 'slave' 节点。");
            }
            System.out.println("集群正在以 [Master-Slave] 模式运行。Master节点是: " + masterNode.getNodeId());
        } else {
            System.out.println("集群正在以 [Sharding] 模式运行。");
        }
    }

    /**
     * 获取 ClusterManager 的单例实例。
     * 这是首次获取实例时必须使用的方法。
     * @param selfNodeId 当前节点的ID，首次调用时必须提供以完成初始化。
     * @return ClusterManager 的唯一实例。
     */
    public static synchronized ClusterManager getInstance(String selfNodeId) {
        if (instance == null) {
            instance = new ClusterManager(selfNodeId);
        }
        return instance;
    }

    /**
     * 获取 ClusterManager 的单例实例（假设已被初始化）。
     * @return ClusterManager 实例。
     * @throws IllegalStateException 如果尚未通过 getInstance(selfNodeId) 初始化。
     */
    public static synchronized ClusterManager getInstance() {
        if (instance == null) {
            throw new IllegalStateException("ClusterManager 尚未初始化。请先调用 getInstance(selfNodeId)。");
        }
        return instance;
    }

    /**
     * 从 classpath 中的 `cluster.properties` 文件加载集群配置。
     * @param selfNodeId 当前节点的ID。
     * @return 检测到的集群模式 (ClusterMode)。
     */
    private ClusterMode loadClusterConfig(String selfNodeId) {
        Properties props = new Properties();
        try (InputStream input = getClass().getClassLoader().getResourceAsStream("cluster.properties")) {
            if (input == null) {
                System.err.println("错误: 找不到 'cluster.properties' 配置文件。请在 resources 目录下创建它。");
                return ClusterMode.SHARDING; // 如果找不到配置文件，默认为分片模式
            }
            props.load(input);

            // 1. 读取集群模式，如果未指定，默认为 "sharding"
            String mode = props.getProperty("cluster.mode", "sharding").toUpperCase();
            ClusterMode detectedMode = ClusterMode.valueOf(mode);

            // 2. 遍历所有属性，解析以 "node." 开头的节点定义
            // 示例: node.node1=127.0.0.1:10001:9991:master
            for (String key : props.stringPropertyNames()) {
                if (key.startsWith("node.")) {
                    String nodeId = key.substring("node.".length());
                    String[] value = props.getProperty(key).split(":");

                    // 【修改】配置格式现在支持4个部分用于主从模式，但为了兼容旧格式，做了灵活处理。
                    if (value.length < 3) {
                        throw new RuntimeException("配置格式错误 in cluster.properties for key '" + key + "'. 期望格式: host:interNodePort:clientPort[:role]");
                    }
                    String host = value[0];
                    int interNodePort = Integer.parseInt(value[1]); // 节点间通信端口
                    int clientPort = Integer.parseInt(value[2]);    // 客户端连接端口
                    // 如果第4部分存在，则为角色；否则默认为普通成员 "member"
                    String role = (value.length > 3) ? value[3] : "member";

                    NodeInfo nodeInfo = new NodeInfo(nodeId, host, interNodePort, clientPort, role);
                    clusterNodes.put(nodeId, nodeInfo);

                    // 3. 如果是主从模式，根据角色信息记录 Master 和 Slave 节点
                    if (detectedMode == ClusterMode.MASTER_SLAVE) {
                        if ("master".equalsIgnoreCase(role)) {
                            if (this.masterNode != null) {
                                throw new IllegalStateException("主从模式配置错误: 只能有一个 'master' 节点。");
                            }
                            this.masterNode = nodeInfo;
                        } else if ("slave".equalsIgnoreCase(role)) {
                            this.slaveNodes.put(nodeId, nodeInfo);
                        }
                    }
                }
            }
            return detectedMode;
        } catch (Exception e) {
            throw new RuntimeException("加载 'cluster.properties' 配置文件失败。", e);
        }
    }

    // --- 【新增】用于获取主从模式信息的 Getter 方法 ---

    public ClusterMode getClusterMode() {
        return clusterMode;
    }

    public NodeInfo getMasterNode() {
        if (clusterMode != ClusterMode.MASTER_SLAVE) {
            throw new IllegalStateException("仅在 Master-Slave 模式下才能获取 Master 节点。");
        }
        return masterNode;
    }

    public Map<String, NodeInfo> getSlaveNodes() {
        if (clusterMode != ClusterMode.MASTER_SLAVE) {
            throw new IllegalStateException("仅在 Master-Slave 模式下才能获取 Slave 节点列表。");
        }
        return slaveNodes;
    }

    /**
     * 判断当前节点是否为 Master 节点。
     */
    public boolean isMaster() {
        return selfNode.equals(masterNode);
    }

    // --- 现有方法保持不变 ---

    public NodeInfo getSelfNode() {
        return selfNode;
    }

    public Map<String, NodeInfo> getClusterNodes() {
        return clusterNodes;
    }

    /**
     * 获取除自身以外的其他所有节点。
     * @return 一个包含其他所有节点的Map。
     */
    public Map<String, NodeInfo> getOtherNodes() {
        return clusterNodes.entrySet().stream()
                .filter(entry -> !entry.getKey().equals(selfNode.getNodeId()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    public NodeInfo getNodeById(String nodeId) {
        return clusterNodes.get(nodeId);
    }
}